/**
 * 
 */
package net.sf.cldsql.dyna.utl;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.cldsql.dj.db.ConnPool;
import net.sf.cldsql.dj.db.DbConnection;
import net.sf.cldsql.dj.mvc.DiContextServletImpl;
import net.sf.cldsql.dj.utl.DiConf;
import net.sf.cldsql.dj.utl.DiException;

/**
 * DynaContext
 */
public class DynaContext extends DiContextServletImpl {

    final Logger log = LoggerFactory.getLogger(DynaContext.class);
    private ArrayList<UsedConn> usedConnList = new ArrayList<UsedConn>();

    /** @param request
    /** @param response
    /** @param serverLet */
    public DynaContext(HttpServletRequest request, HttpServletResponse response, HttpServlet serverLet) {
        super(request, response, serverLet);
    }

    /** @return */
    public DiConf getConf() {
        ServletContext servletContext = serverLet.getServletContext();
        DynaConf conf;
        if ((conf = (DynaConf)servletContext.getAttribute(DynaConf.DYNA_CONFIG)) == null) {
            synchronized (servletContext) {
                if ((conf = (DynaConf)servletContext.getAttribute(DynaConf.DYNA_CONFIG)) == null) {
                    log.info("new and init Conf.");
                    conf = new DynaConf(serverLet.getInitParameter(DynaConf.DYNA_CONFIG));
                    servletContext.setAttribute(DynaConf.DYNA_CONFIG, conf);
                }
            }
        }
        return conf;
    }

    /** @return */
    @SuppressWarnings("unchecked")
    private HashMap<String, ConnPool> getConnPoolMap() {
        ServletContext servletContext = serverLet.getServletContext();
        HashMap<String, ConnPool> poolMap;
        if ((poolMap = (HashMap<String, ConnPool>)servletContext.getAttribute(DynaConf.DYNA_CONN_POOL_MAP_KEY)) == null) {
            synchronized (servletContext) {
                if ((poolMap = (HashMap<String, ConnPool>)servletContext.getAttribute(DynaConf.DYNA_CONN_POOL_MAP_KEY)) == null) {
                    log.info("new and init connPoolMap.");
                    poolMap = new HashMap<String, ConnPool>();
                    DiConf conf = getConf();
                    String[] poolNames = conf.get(DiConf.SQL_POOLS).split(",");
                    for (String poolName : poolNames) {
                        log.info("new and init ConnPool {}.", poolName);
                        ConnPool pool = new ConnPool (
                            poolName,
                            conf.get(poolName, DiConf.JDBC_DBMS),
                            conf.get(poolName, DiConf.JDBC_DRIVER),
                            conf.get(poolName, DiConf.JDBC_URL),
                            conf.get(poolName, DiConf.JDBC_USERNAME),
                            conf.get(poolName, DiConf.JDBC_PASSWORD),
                            Integer.parseInt(conf.get(poolName, DiConf.MAX_CONN)),
                            conf.get(poolName, DiConf.JDBC_RSET_NAMING_STYLE),
                            conf.get(poolName, DiConf.MAX_RETRIES) == null ? 0 : Integer.parseInt(conf.get(poolName, DiConf.MAX_RETRIES)),
                            DiConf.JDBC_AUTO_COMMIT_TRUE.equals(conf.get(poolName, DiConf.JDBC_AUTO_COMMIT))  
                        );
                        poolMap.put(poolName, pool);
                    }
                    servletContext.setAttribute(DynaConf.DYNA_CONN_POOL_MAP_KEY, poolMap);
                }
            }
        }
        return poolMap;
    }

    /** @param string */
    private ConnPool getConnPool(String poolName) {
        return getConnPoolMap().get(poolName.equals("") ? DiConf.SQL_POOL_DEFAULT : poolName);
    }

    class UsedConn {
        private ConnPool pool;
        private DbConnection conn;

        /** @return conn */
        public DbConnection getConn() {
            return conn;
        }

        /** @return pool */
        public ConnPool getPool() {
            return pool;
        }

        UsedConn (ConnPool pool, DbConnection conn) {
            this.pool = pool;
            this.conn = conn;
        }
    }
    
    public DbConnection getConn(String poolName) {
        ConnPool pool = getConnPool(poolName);
        DbConnection conn = pool.get();
        usedConnList.add(new UsedConn(pool, conn));
        return conn;
    }
    
    public void commitConns() throws DiException {
        Exception exception = null;
        for (UsedConn usedConn : usedConnList) {
            DbConnection conn = usedConn.getConn();
            try {
                conn.getConn().commit();
            } catch (Exception e) {
                log.error("when commit connection {}, exception occured {}", conn, e);
                exception = e;
            }
        }
        if (exception != null) {
            throw new DiException(exception);
        }
    }

    public void rollbackConns() {
        Exception exception = null;
        for (UsedConn usedConn : usedConnList) {
            DbConnection conn = usedConn.getConn();
            try {
                conn.getConn().rollback();
            } catch (Exception e) {
                log.error("when rollback connection {}, exception occured {}", conn, e);
                exception = e;
            }
        }
        if (exception != null) {
            throw new DiException(exception);
        }
    }

    /**  */
    public void releaseConns() {
        Exception exception = null;
        for (UsedConn usedConn : usedConnList) {
            DbConnection conn = usedConn.getConn();
            try {
                usedConn.getPool().release(conn);
            } catch (Exception e) {
                log.error("when release connection {}, exception occured {}", conn, e);
                exception = e;
            }
        }
        if (exception != null) {
            throw new DiException(exception);
        }
    }
    
}
